/*
 *  Copyright (C) 2008-2009 Andrej Stepanchuk
 *  Copyright (C) 2009-2010 Howard Chu
 *
 *  This file is part of librtmp.
 *
 *  librtmp is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1,
 *  or (at your option) any later version.
 *
 *  librtmp is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with librtmp see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA  02110-1301, USA.
 *  http://www.gnu.org/copyleft/lgpl.html
 */

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>

#include "rtmp_sys.h"
#include "log.h"

#define MAX_PRINT_LEN	2048

RTMP_LogLevel RTMP_debuglevel=RTMP_LOGERROR;

static int neednl;

static FILE *fmsg;

static RTMP_LogCallback rtmp_log_default,*cb=rtmp_log_default;

static const char *levels[]={"CRIT","ERROR","WARNING","INFO","DEBUG","DEBUG2"};

static void
rtmp_log_default(int level,const char *format,va_list vl)
{
	char str[MAX_PRINT_LEN]="";

	vsnprintf(str,MAX_PRINT_LEN-1,format,vl);

	/* Filter out 'no-name' */
	if(RTMP_debuglevel<RTMP_LOGALL&&strstr(str,"no-name")!=NULL)
		return;

	if(!fmsg)
		fmsg=stderr;

	if(level<=RTMP_debuglevel)
	{
		if(neednl)
		{
			putc('\n',fmsg);
			neednl=0;
		}
		fprintf(fmsg,"%s: %s\n",levels[level],str);
#ifdef _DEBUG
		fflush(fmsg);
#endif
	}
}

void
RTMP_LogSetOutput(FILE *file)
{
	fmsg=file;
}

void
RTMP_LogSetLevel(RTMP_LogLevel level)
{
	RTMP_debuglevel=level;
}

void
RTMP_LogSetCallback(RTMP_LogCallback *cbp)
{
	cb=cbp;
}

RTMP_LogLevel
RTMP_LogGetLevel()
{
	return RTMP_debuglevel;
}

void
RTMP_Log(int level,const char *format,...)
{
	va_list args;
	va_start(args,format);
	cb(level,format,args);
	va_end(args);
}

static const char hexdig[]="0123456789abcdef";

void
RTMP_LogHex(int level,const uint8_t *data,unsigned long len)
{
	unsigned long i;
	char line[50],*ptr;

	if(level>RTMP_debuglevel)
		return;

	ptr=line;

	for(i=0;i<len;i++)
	{
		*ptr++=hexdig[0x0f&(data[i]>>4)];
		*ptr++=hexdig[0x0f&data[i]];
		if((i&0x0f)==0x0f)
		{
			*ptr='\0';
			ptr=line;
			RTMP_Log(level,"%s",line);
		}
		else
			*ptr++=' ';
	}
	if(i&0x0f)
	{
		*ptr='\0';
		RTMP_Log(level,"%s",line);
	}
}

void
RTMP_LogHexString(int level,const uint8_t *data,unsigned long len)
{
#define BP_OFFSET 9
#define BP_GRAPH 60
#define BP_LEN	80
	char line[BP_LEN];
	unsigned long i;

	if(!data||level>RTMP_debuglevel)
		return;

	/* in case len is zero */
	line[0]='\0';

	for(i=0 ;i<len ;i++)
	{
		int n=i%16;
		unsigned off;

		if(!n)
		{
			if(i)
				RTMP_Log(level,"%s",line);
			memset(line,' ',sizeof(line)-2);
			line[sizeof(line)-2]='\0';

			off=i%0x0ffffU;

			line[2]=hexdig[0x0f&(off>>12)];
			line[3]=hexdig[0x0f&(off>>8)];
			line[4]=hexdig[0x0f&(off>>4)];
			line[5]=hexdig[0x0f&off];
			line[6]=':';
		}

		off=BP_OFFSET+n*3+((n>=8)?1:0);
		line[off]=hexdig[0x0f&(data[i]>>4)];
		line[off+1]=hexdig[0x0f&data[i]];

		off=BP_GRAPH+n+((n>=8)?1:0);

		if(isprint(data[i]))
			line[BP_GRAPH+n]=data[i];
		else
			line[BP_GRAPH+n]='.';
	}

	RTMP_Log(level,"%s",line);
}

/* These should only be used by apps, never by the library itself */
void
RTMP_LogPrintf(const char *format,...)
{
	char str[MAX_PRINT_LEN]="";
	int len;
	va_list args;
	va_start(args,format);
	len=vsnprintf(str,MAX_PRINT_LEN-1,format,args);
	va_end(args);

	if(RTMP_debuglevel==RTMP_LOGCRIT)
		return;

	if(!fmsg)
		fmsg=stderr;

	if(neednl)
	{
		putc('\n',fmsg);
		neednl=0;
	}

	if(len>MAX_PRINT_LEN-1)
		len=MAX_PRINT_LEN-1;
	fprintf(fmsg,"%s",str);
	if(str[len-1]=='\n')
		fflush(fmsg);
}

void
RTMP_LogStatus(const char *format,...)
{
	char str[MAX_PRINT_LEN]="";
	va_list args;
	va_start(args,format);
	vsnprintf(str,MAX_PRINT_LEN-1,format,args);
	va_end(args);

	if(RTMP_debuglevel==RTMP_LOGCRIT)
		return;

	if(!fmsg)
		fmsg=stderr;

	fprintf(fmsg,"%s",str);
	fflush(fmsg);
	neednl=1;
}
